using System;
using System.Diagnostics;
using System.Windows.Forms;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Ruminate.GUI.Framework;
using Ruminate.Utils;
using Color = Microsoft.Xna.Framework.Color;
using Timer = System.Timers.Timer;
using MouseEventArgs = Microsoft.Xna.Framework.Input.MouseEventArgs;
using KeyEventArgs = Microsoft.Xna.Framework.Input.KeyEventArgs;
using Keys = Microsoft.Xna.Framework.Input.Keys;

// TODO
// [1]: New skin with font, cursor, and color
// [2]: Handle words longer than the width of the box.
// [3]: Handle keyboard up and down
// 4: Event for exceeding bounds (sounds?) and done for going over (alert message?)
// [5]: Handle keyboard page up and page down
// [6]: Handle keyboard home and end
// [7]: Handle selection
// [8]: Handle clipboard
// 9: Handle undo / redo

namespace Ruminate.GUI.Content
{
    /// <summary>
    /// Plain text box.
    /// </summary>
    public class TextBox : Element, IDisposable {

        /*####################################################################*/
        /*                               Variables                            */
        /*####################################################################*/
        
        #region Variables

        /* ## Local Variables ## */
        private readonly Timer _cursorTimer;
        private Texture2D _cursor, _text, _panel;
        private SpriteFont _font;
        private bool _showCursor, _selectText;
        private BorderRenderer _renderer;   

        private TextData _textData;
        private struct TextData {

            private int _textCursor;
            public int TextCursor {
                get {
                    return _textCursor;
                } set {
                    if (value > Length) {
                        _textCursor = Length;
                    } else if (value < 0) {
                        _textCursor = 0;
                    } else {
                        _textCursor = value;
                    }
                }
            }

            private short _length;
            public short Length {
                get {
                    return _length;
                } set {
                    if (TextCursor > value) {
                        TextCursor = value; 
                    } 

                    _length = value;
                }
            }

            public short MaxLength, TextHeight, SelectedChar1, SelectedChar2;
            public char[] Char;
            public short[] CharX, CharY;
            public byte[] CharWidth, Row;
        };

        /* ## Exposed Variables ## */
        public string Value {
            get {
                return new string(_textData.Char);
            } set {
                lock (typeof(TextData)) {
                    BuildTextData(value);
                    if (InstanceData != null) RenderText();
                }
            }
        }

        #endregion

        /*####################################################################*/
        /*                               Mini API                             */
        /*####################################################################*/

        #region Miniture API

        private int InsertCharacter(char character, int location) {

            lock (typeof(TextData)) {

                //Can't insert if already at the maximum length
                if (!(_textData.Length < _textData.MaxLength)) return location;

                //Make sure the location of the cursor is safe
                location = (location == -1) ? _textData.Length : location;

                //Slide everything right once then insert the character into the gap
                Array.Copy(
                    _textData.Char,
                    location,
                    _textData.Char,
                    location + 1,
                    _textData.Length - location);
                _textData.Char[location] = character;

                Array.Copy(
                    _textData.CharWidth,
                    location,
                    _textData.CharWidth,
                    location + 1,
                    _textData.Length - location);

                _textData.CharWidth[location] = MeasureChar(location);

                //Text is now longer
                _textData.Length++;
            }

            //Cursor needs to shift right
            return location + 1;
        }

        private int RemoveCharactor(int location) {

            lock (typeof(TextData)) {

                if (location > _textData.Length) { return location; }

                //Shift everything 1 left after character after cursor
                Array.Copy(
                    _textData.Char,
                    location + 1,
                    _textData.Char,
                    location,
                    _textData.Length - location - 1);

                Array.Copy(
                    _textData.CharWidth,
                    location + 1,
                    _textData.CharWidth,
                    location,
                    _textData.Length - location - 1);

                //Text is now shorter
                _textData.Length--;

                //Measure the character again in case it is affected by it neighbors
                if (location > 0) {

                    _textData.CharWidth[location - 1] = MeasureChar(location);
                }
            }

            //No need to move the cursor
            return location;
        }

        /// <summary>
        /// Gracefully exits if nothing is selected.
        /// </summary>
        private int HandelSelected(int cursor, bool clipboard, bool remove) {
            //Nothing selected so return
            if (_textData.SelectedChar1 == -1 || _textData.SelectedChar2 == -1) { return cursor; }

            //Start the cut at which ever selector is smaller
            var startCut = (_textData.SelectedChar1 < _textData.SelectedChar2)
                ? _textData.SelectedChar1
                : _textData.SelectedChar2;

            //Start the cut at which ever selector is larger
            var endCut = (_textData.SelectedChar1 < _textData.SelectedChar2)
                ? _textData.SelectedChar2
                : _textData.SelectedChar1;

            //Copy to clipboard if needed
            if (clipboard) {
                Clipboard.SetDataObject(Value.Substring(startCut, (endCut + 1) - startCut));
            }

            //Remove is necessary
            if (remove) {
                Value = Value.Remove(startCut, (endCut + 1) - startCut);
                _textData.TextCursor = startCut;

                _textData.SelectedChar1 = -1;
                _textData.SelectedChar2 = -1;

                if (cursor >= startCut && cursor <= endCut) {
                    return startCut;
                }
            }

            return cursor;
        }

        private void ResetTimer() {

            _showCursor = true;
            _cursorTimer.Interval = 500;
        }

        private void BuildTextData(string value) {

            var x = value.IndexOf('\0');
            if (x != -1) { value = value.Substring(0, x); }

            _textData.Length = (short)value.Length;
            _textData.TextHeight = (short)_font.MeasureString(" ").Y;
            _textData.Char = new char[_textData.MaxLength];
            _textData.CharX = new short[_textData.MaxLength];
            _textData.CharY = new short[_textData.MaxLength];
            _textData.CharWidth = new byte[_textData.MaxLength];
            _textData.Row = new byte[_textData.MaxLength];
            _textData.TextCursor = -1;
            _textData.SelectedChar1 = -1;
            _textData.SelectedChar2 = -1;

            value.ToCharArray().CopyTo(_textData.Char, 0);

            var iCount = 0;

            while (iCount < _textData.Length) {

                _textData.CharWidth[iCount] = MeasureChar(iCount);
                iCount++;
            }
        }

        private byte MeasureChar(int location) {

            var value = new string(_textData.Char);
            var front = _font.MeasureString(value.Substring(0, location)).X;
            var end = _font.MeasureString(value.Substring(0, location + 1)).X;

            return (byte)(end - front);
        }

        #endregion 

        /*####################################################################*/
        /*                        Node Initialization                         */
        /*####################################################################*/

        #region Node Initialization

        public TextBox(int x, int y, int width, int height, short maxChars) 
            : base(new Offset(x, y, width, height)) {

            _textData = new TextData {
                MaxLength = maxChars
            };

            _cursorTimer = new Timer(500);
            _cursorTimer.Elapsed += delegate { _showCursor = !_showCursor; };
            _cursorTimer.Start();
        }

        public TextBox(int buffer, short maxChars) : base(new Fill(buffer)) {

            _textData = new TextData {
                MaxLength = maxChars
            };

            _cursorTimer = new Timer(500);
            _cursorTimer.Elapsed += delegate { _showCursor = !_showCursor; };
            _cursorTimer.Start();
        }

        public void Dispose() {
            _cursorTimer.Dispose();
        }

        #endregion

        /*####################################################################*/
        /*                       Widget Initialization                        */
        /*####################################################################*/

        #region Widget Initialization

        protected internal override void Init() {

            _renderer = (BorderRenderer)InstanceData.DefaultRenderers["Panel"];
            var holder = (TextRenderer)InstanceData.DefaultRenderers["TextRenderer"];
            _cursor = holder.Cursor;
            _font = holder.Font;

            BuildTextData(string.Empty);
        }

        protected internal override void Render() {

            var size = LocationComponent.Size.ToPoint();
            _panel = _renderer.Render(size);
            RenderText();
        }

        protected internal override Rectangle ChildRenderArea() {

            return _renderer.BuildSafeArea(LocationComponent.Size.ToPoint());
        }

        #endregion

        /*####################################################################*/
        /*                                Events                              */
        /*####################################################################*/

        #region Events

        protected internal override void CharEntered(CharacterEventArgs e) {

            lock (typeof(TextData)) {

                switch (e.Character) {
                    case '\b': //Backspace <= remove selected or remove before cursor
                        if (_textData.SelectedChar1 != -1 && _textData.SelectedChar2 != -1) {
                            //Remove is there is a selection
                            HandelSelected(_textData.TextCursor, false, true);
                        } else if (_textData.TextCursor > 0 && _textData.Length > 0) {
                            _textData.TextCursor = RemoveCharactor(_textData.TextCursor - 1);
                        }
                        break;
                    case '\n': //New Line <= Insert character after cursor
                    case '\r':
                        _textData.TextCursor = InsertCharacter(e.Character, _textData.TextCursor);
                        break;
                    case '\t': //Tabs currently not supported
                        break;
                    case (char)3: //Copy
                        _textData.TextCursor = HandelSelected(_textData.TextCursor, true, false);
                        break;
                    case (char)22: //Paste                        
                        var dataObject = Clipboard.GetDataObject();
                        if (dataObject != null) {
                            var text = dataObject.GetData(DataFormats.Text).ToString();
                            if (text.Length + _textData.Length < _textData.MaxLength) {
                                var cursor = _textData.TextCursor;
                                Value = Value.Insert(_textData.TextCursor, text);
                                _textData.TextCursor = cursor + text.Length;
                            }
                        }
                        break;
                    case (char)24: //Cut    
                        _textData.TextCursor = HandelSelected(_textData.TextCursor, true, true);
                        break;
                    default:
                        //Should prevent all of the edge cases that kept causing crashes
                        if (char.IsLetterOrDigit(e.Character) || char.IsPunctuation(e.Character)
                            || char.IsSymbol(e.Character) || char.IsWhiteSpace(e.Character)) {

                            _textData.TextCursor = InsertCharacter(e.Character, _textData.TextCursor);
                        }
                        break;
                }
                RenderText();
            }

            ResetTimer();
        }

        protected internal override void MouseDown(MouseEventArgs e) {

            _selectText = false;

            lock (typeof(TextData)) {

                //Rest selection
                _textData.SelectedChar1 = -1;
                _textData.SelectedChar2 = -1;

                //Clicked outside of the box so exit
                if (!LocationComponent.RenderBounds.Contains(e.Location)) { return; }

                var rectangle = new Rectangle(0, 0, 0, _textData.TextHeight);

                for (short i = 0; i <= _textData.Length; i++) {

                    //Rectangle that encompasses the current character
                    rectangle.X = _textData.CharX[i] + (int)LocationComponent.RenderLocation.X;
                    rectangle.Y = _textData.CharY[i] + (int)LocationComponent.RenderLocation.Y;
                    rectangle.Width = _textData.CharWidth[i];

                    //Click on a character so put the cursor in front of it
                    if (rectangle.Contains(e.Location)) {

                        _selectText = true;
                        _textData.SelectedChar1 = i;
                        return;
                    }

                    //If clicked on this row and next character is on next row
                    //then select this character. 
                    if (_textData.Row[i + 1] != _textData.Row[i]) {
                        if (_textData.Row[i + 1] == 0) {

                            _selectText = true;
                            _textData.SelectedChar1 = _textData.Length;
                            return;
                        } else if ((rectangle.Y <= e.Location.Y) && (e.Location.Y <= rectangle.Y + rectangle.Height)) {

                            _selectText = true;
                            _textData.SelectedChar1 = i;
                            return;
                        }
                    }
                }
            }
        }

        protected internal override void MouseMove(MouseEventArgs e) {

            if (!_selectText) { return; }

            lock (typeof(TextData)) {

                //Possible for this to get changed between click and move so leave if need be
                if (_textData.SelectedChar1 == -1) { return; }

                var rectangle = new Rectangle(
                    _textData.CharX[_textData.SelectedChar1] + (int)LocationComponent.RenderLocation.X,
                    _textData.CharY[_textData.SelectedChar1] + (int)LocationComponent.RenderLocation.Y,
                    _textData.CharWidth[_textData.SelectedChar1],
                    _textData.TextHeight);

                //Same location as mouse down so no selection just set the cursor
                if (!rectangle.Contains(e.Location)) {

                    for (short i = 0; i <= _textData.Length; i++) {

                        //Rectangle that encompasses the current character
                        rectangle.X = _textData.CharX[i] + (int)LocationComponent.RenderLocation.X;
                        rectangle.Y = _textData.CharY[i] + (int)LocationComponent.RenderLocation.Y;
                        rectangle.Width = _textData.CharWidth[i];

                        //Click on a character so put the cursor in front of it
                        if (rectangle.Contains(e.Location)) {
                            _textData.SelectedChar2 = i;
                            return;
                        }
                    }
                }
            }
        }

        protected internal override void MouseUp(MouseEventArgs e) {

            _selectText = false;

            lock (typeof(TextData)) {

                if (_textData.SelectedChar1 == -1) { return; }

                if (_textData.SelectedChar1 == _textData.Length) {

                    ResetTimer();
                    _textData.TextCursor = _textData.Length;
                    _textData.SelectedChar1 = -1;
                    return;
                }

                var rectangle = new Rectangle(
                    _textData.CharX[_textData.SelectedChar1] + (int)LocationComponent.RenderLocation.X,
                    _textData.CharY[_textData.SelectedChar1] + (int)LocationComponent.RenderLocation.Y,
                    _textData.CharWidth[_textData.SelectedChar1],
                    _textData.TextHeight);

                //Same location as mouse down so no selection just set the cursor
                if (rectangle.Contains(e.Location)) {

                    ResetTimer();
                    _textData.TextCursor = _textData.SelectedChar1;
                    _textData.SelectedChar1 = -1;
                    return;
                }

                //If clicked on this row and next character is on next row
                //then select this character. 
                if (_textData.Row[_textData.SelectedChar1 + 1] != _textData.Row[_textData.SelectedChar1]) {
                    if ((rectangle.Y <= e.Location.Y) && (e.Location.Y <= rectangle.Y + rectangle.Height)) {

                        ResetTimer();
                        _textData.TextCursor = _textData.SelectedChar1;
                        return;
                    }
                }
            }
        }

        protected internal override void KeyDown(KeyEventArgs e) {

            lock (typeof(TextData)) {
                if (_textData.Length != 0) {
                    switch (e.KeyCode) {
                        case Keys.Left:
                            _textData.TextCursor--;
                            break;
                        case Keys.Right:
                            _textData.TextCursor++;
                            break;
                        case Keys.Up:
                            //Current Row
                            var upRow = (_textData.TextCursor == _textData.Length)
                                ? _textData.Row[_textData.TextCursor - 1]
                                : _textData.Row[_textData.TextCursor];

                            //We are on the top row so we are break
                            if (upRow == 0) { break; }

                            //Collision data
                            var upVertical = (_textData.TextCursor == _textData.Length)
                                ? _textData.CharX[_textData.TextCursor - 1]
                                : _textData.CharX[_textData.TextCursor];

                            //Starting at last character backtrack until we find a 
                            //suitable spot. Upon not finding any jump to end of 
                            //above line.
                            for (var i = _textData.TextCursor; i >= 0; i--) {

                                //Row above and further left than current cursor location so 
                                //a good location
                                if (_textData.Row[i] == upRow - 1 && _textData.CharX[i] <= upVertical) {
                                    _textData.TextCursor = i;
                                    break;
                                }
                            }
                            break;
                        case Keys.Down:
                            var downCursor = (_textData.TextCursor == _textData.MaxLength)
                                ? _textData.TextCursor - 1
                                : _textData.TextCursor;

                            //Current Row
                            var downRow = _textData.Row[downCursor];

                            //We are on the bottom row so break
                            if (downRow == _textData.Row[_textData.Length - 1]) { break; }

                            //Collision data
                            var downVertical = _textData.CharX[downCursor];

                            //Starting at last character progress until we find a 
                            //suitable spot. Upon not finding any jump to end of 
                            //below line.
                            for (var i = _textData.Length; i >= 0; i--) {

                                //Row above and further left than current cursor location so 
                                //a good location
                                if (_textData.Row[i] == downRow + 1 && _textData.CharX[i] <= downVertical) {
                                    _textData.TextCursor = i;
                                    break;
                                }
                            }

                            //Handel edge case of after last character
                            if (_textData.Row[_textData.Length - 1] == downRow + 1
                                && _textData.CharX[_textData.Length] + _textData.CharWidth[_textData.Length]
                                <= downVertical) {

                                _textData.TextCursor = _textData.Length;
                            }
                            break;
                        case Keys.Home:
                            var homeCursor = (_textData.TextCursor == _textData.MaxLength)
                                ? _textData.TextCursor - 1
                                : _textData.TextCursor;

                            var homeRow = _textData.Row[homeCursor];
                            var homeStart = homeCursor;

                            //Jump to beginning of text by default
                            _textData.TextCursor = 0;

                            //Jump to start of line if possible. Will fall though if not possible
                            for (var i = homeStart; i > 0; i--) {
                                if (_textData.Row[i] < homeRow) {
                                    _textData.TextCursor = i + 1;
                                    break;
                                }
                            }
                            break;
                        case Keys.End:
                            var endCursor = (_textData.TextCursor == _textData.MaxLength)
                                ? _textData.TextCursor - 1
                                : _textData.TextCursor;

                            var endRow = _textData.Row[endCursor];
                            var endStart = endCursor;

                            //Jump to end of text by default
                            _textData.TextCursor = _textData.Length;

                            //Jump to end of line if possible. Will fall though if not possible
                            for (var i = endStart; i < _textData.Length; i++) {
                                if (_textData.Row[i] <= endRow) { continue; }
                                _textData.TextCursor = i - 1;
                                break;
                            }
                            break;
                        case Keys.PageUp:
                            _textData.TextCursor = 0;
                            break;
                        case Keys.PageDown:
                            _textData.TextCursor = _textData.Length;
                            break;
                        case Keys.Delete:
                            if (_textData.SelectedChar1 != -1 && _textData.SelectedChar2 != -1) {
                                //Remove is there is a selection
                                HandelSelected(_textData.TextCursor, false, true);
                            } else if (_textData.TextCursor < _textData.Length) {
                                //Else remove character on cursor as long as cursor is past characters 
                                RemoveCharactor(_textData.TextCursor);
                            }

                            RenderText();
                            break;
                    }
                }
            }

            ResetTimer();
        }

        #endregion

        /*####################################################################*/
        /*                              Game Loop                             */
        /*####################################################################*/

        #region Game Loop

        protected internal override void Update() { }

        protected internal override void Draw() {

            SpriteBatch.Draw(_panel, LocationComponent.RenderBounds, Color.White);
            SpriteBatch.Draw(_text, LocationComponent.RenderSafeAreaLocation, Color.White);            
            RenderCursor();
            RenderSelection();
            DrawDebug();
        }

        #endregion

        /*####################################################################*/
        /*                           Rendering API                            */
        /*####################################################################*/

        #region Rendering API

        /// <summary>
        /// Renders the current text to a Texture2D
        /// </summary>
        private void RenderText() {

            var textureBaker = new TextureBaker(
                InstanceData.GraphicsDevice,
                LocationComponent.Size,
                RenderState.Fill);

            var start = 0;
            var height = 0.0f;

            while (true) {

                start = RenderLine(textureBaker, start, height);                

                if (start >= _textData.Length) {
                    _text = textureBaker.GetTexture();
                    return;
                }

                height += _textData.TextHeight;
            }            
        }

        /// <summary>
        /// Renders a line of text. Returns the 
        /// </summary>
        private int RenderLine(TextureBaker textureBaker, int start, float height) {

            var breakLocation = start;
            var lineLength = 0.0f;
            var row = (byte)(height / _textData.TextHeight);
            string text = new string(_textData.Char), tempText;

            //Starting from end of last line loop though the characters
            for (var iCount = start; iCount < _textData.Length; iCount++) {

                //Calculate screen location of current character
                _textData.CharX[iCount] = (short)lineLength;
                _textData.CharY[iCount] = (short)height;
                _textData.Row[iCount] = row;

                //Calculate the width of the current line
                lineLength += _textData.CharWidth[iCount];

                //Current line is too long need to split it
                if (lineLength > LocationComponent.RenderSafeArea.Width) {
                    if (breakLocation == start) { //Have to split a word
                        //Render line and return start of new line
                        tempText = text.Substring(start, iCount - start);
                        textureBaker.DrawString(_font, tempText, new Vector2(0.0f, height), Color.Black);
                        return iCount + 1;
                    } else { //Have a character we can split on
                        //Render line and return start of new line
                        tempText = text.Substring(start, breakLocation - start);
                        textureBaker.DrawString(_font, tempText, new Vector2(0.0f, height), Color.Black);
                        return breakLocation + 1;
                    }
                }

                //Handle characters that force/allow for breaks
                switch (_textData.Char[iCount]) {
                    case '\r': //These characters force a line break
                    case '\n':
                        //Render line and return start of new line
                        tempText = text.Substring(start, iCount - start);
                        textureBaker.DrawString(_font, tempText, new Vector2(0.0f, height), Color.Black);
                        return iCount + 1;
                    case '-': //These characters are good break locations
                    case ' ':                        
                        breakLocation = iCount;
                        break;
                }
            }

            //We hit the end of the text box render line and return
            //_textData.Length so RenderText knows to return
            tempText = text.Substring(start, _textData.Length - start);
            textureBaker.DrawString(_font, tempText, new Vector2(0.0f, height), Color.Black);
            return _textData.Length;
        }

        /// <summary>
        /// Renders the Cursor
        /// 
        /// Edge Cases:
        /// Cursor after new line character (Put on new line)
        /// Cursor = length (Place at end of last char)
        /// All other Locations (Draw at beginning of Cursor location)
        /// </summary>
        private void RenderCursor() {

            if (!_showCursor) { return; }

            int x = (int)LocationComponent.RenderSafeAreaLocation.X,
                y = (int)LocationComponent.RenderSafeAreaLocation.Y;

            if (_textData.TextCursor > 0) {
                if (_textData.TextCursor > 0 && (_textData.Char[_textData.TextCursor - 1] == '\n'
                    || _textData.Char[_textData.TextCursor - 1] == '\r')) {

                    //Beginning of next line
                    y += _textData.CharY[_textData.TextCursor - 1] + _textData.TextHeight;
                } else if (_textData.TextCursor == _textData.Length) {

                    //After last character
                    x += _textData.CharX[_textData.TextCursor - 1]
                       + _textData.CharWidth[_textData.TextCursor - 1];
                    y += _textData.CharY[_textData.TextCursor - 1];
                } else {

                    //Beginning of current character                
                    x += _textData.CharX[_textData.TextCursor];
                    y += _textData.CharY[_textData.TextCursor];
                }
            }

            SpriteBatch.Draw(
                _cursor,
                new Rectangle(x, y, _cursor.Width, _textData.TextHeight),
                null, 
                Color.Black, 
                0.0f,
                new Vector2(_cursor.Width / 2, 0), 
                SpriteEffects.None, 
                0);
        }

        /// <summary>
        /// Renders the selection rectangle
        /// I might should make it more transparent
        /// </summary>
        private void RenderSelection() {
            
            if (_textData.SelectedChar1 != -1 && _textData.SelectedChar2 != -1) {

                var rectangle = new Rectangle(0, 0, 0, _textData.TextHeight);

                var start = (_textData.SelectedChar1 < _textData.SelectedChar2)
                    ? _textData.SelectedChar1
                    : _textData.SelectedChar2;

                var end = (_textData.SelectedChar1 < _textData.SelectedChar2)
                    ? _textData.SelectedChar2
                    : _textData.SelectedChar1;

                for (var i = start; i < end; i++) {

                    //Rectangle that encompasses the current character
                    rectangle.X = _textData.CharX[i] + (int)LocationComponent.RenderLocation.X;
                    rectangle.Y = _textData.CharY[i] + (int)LocationComponent.RenderLocation.Y;
                    rectangle.Width = _textData.CharWidth[i];

                    SpriteBatch.Draw(DebugUtils.White, rectangle, Color.LightBlue);
                }
            }
        }

        #endregion
        
        /*####################################################################*/
        /*                                Debug                               */
        /*####################################################################*/
        
        #region Debug

        [Conditional("TEXTBOX")]
        private void DrawDebug() {

            var s = false;

            for (var i = 0; i < _textData.Length; i++) {

                var x = _textData.CharX[i] + LocationComponent.RenderSafeArea.X;
                var y = _textData.CharY[i] + LocationComponent.RenderSafeArea.Y;

                s = !s;
                SpriteBatch.Draw(
                    DebugUtils.White,
                    new Rectangle(x, y, _textData.CharWidth[i], _textData.TextHeight),
                    s ? Color.Green : Color.SkyBlue);

                SpriteBatch.DrawString(
                    _font,
                    "Length = " + _textData.Length + "\n" +
                    "Cursor = " + _textData.TextCursor + "\n" +
                    "Clipboard = " + Clipboard.GetDataObject().GetData(DataFormats.Text),
                    LocationComponent.RenderLocation + new Vector2(0, LocationComponent.Size.Y - 40),
                    Color.Black);
            }
        }

        #endregion        
    }
}